'''
    @project: Masdevallia
    @author: Austin Baker
    @created: 2/22/2014
    @license: http://opensource.org/licenses/mit-license.php

    The MIT License (MIT)

    Copyright (c) 2014 Austin Baker
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
    About:
    Class for manipulating XML files.
    
    Usage:
    filepath = 'E:\\maya\\scripts\\masdevallia\\test.xml'
    newpath = 'E:\\maya\\scripts\\masdevallia\\write_test.xml'
    tmpData = XML().readFile( inFilename=filepath )
    XML().writeFile( inXMLData=tmpData, outFilename=newpath )
'''

'''
============================================================
--->>   IMPORT MODULES
============================================================
'''
import xml.etree.ElementTree as ElementTree
import xml.dom.minidom as MiniDom
import maya.OpenMaya as OpenMaya


'''
============================================================
--->>   GLOBALS
============================================================
'''
DATA_TYPES = {'float':float}


'''
============================================================
--->>   XML CLASS
============================================================
'''
class XML( object ):
    '''
    Class for manipulating XML files.
    '''
    def __init__( self, inXMLData=None ):
        '''
        @param inXMLData: XMLData class. XML Data to be worked with.
        '''
        if inXMLData is None:
            self.data = None
            
        elif isinstance( inXMLData, XMLData ):
            self.data = inXMLData
               
        else:
            OpenMaya.MGlobal.displayError( 'XML.__init__: Argument passed in is of type {0}. Needs to be XMLData.'.format( type( inXMLData ) ) )

    def writeFile( self, **kwargs ):
        '''
        Write to an XML file.
        
        @param inXMLData: XMLData.
        @param outFilename: String. Full path and filename of XML file to save.
        '''
        if len( kwargs ) == 0:
            OpenMaya.MGlobal.displayError( 'XML.writeFile: No keyword argument provided or no XML data.' )
            
        if 'inXMLData' in kwargs:
            data = kwargs[ 'inXMLData' ]
            
        elif self.data:
            data = self.data
            
        if 'outFilename' in kwargs:
            outFilename = kwargs[ 'outFilename' ]
            
        else:
            OpenMaya.MGlobal.displayError( 'XML.writeFile: No output file path provided.' )
            
        root = ElementTree.Element( 'data' )
        
        fileComment = ElementTree.Comment( outFilename )
        root.append( fileComment )
        
        for obj in data.objects:
            newObject = ElementTree.SubElement( root, 'object', attrib={ 'name':obj.name, 'type':obj.type, 'icon':obj.icon } )
            
            for element in obj.elements:
                if type(element) == XMLCurveElement:    
                    newElement = ElementTree.SubElement( newObject, 'elem', attrib={'name':element.name,
                                                                                    'type':element.type,
                                                                                    'degree':str(element.degree),
                                                                                    'form':str(element.form)} )            
                else:
                    newElement = ElementTree.SubElement( newObject, 'elem', attrib={ 'name':element.name, 'type':element.type } )
                
                for attribute in element.attributes:
                    newAttribute = ElementTree.SubElement( newElement, 'attr', attrib={ 'name':attribute.name } )
                    
                    for value in attribute.values:
                        val = ElementTree.SubElement( newAttribute, 'value', attrib={ 'name':value.name, 'type':value.type } )
                        val.text = value.text

        newXML = MiniDom.parseString( ElementTree.tostring( root, 'utf-8' ) )
        f = file( outFilename, 'w' )
        f.write( newXML.toprettyxml() )    
        
    
    def readFile( self, **kwargs ):
        '''
        Read from an XML file.
        
        @param inFilename: String. Full path and filename of XML file.
        @return: XMLData object.
        '''
        if len( kwargs ) == 0:
            OpenMaya.MGlobal.displayError( 'XML.readFile: No XML filename provided.' )
            
        elif 'inFilename' in kwargs:
            filename = kwargs[ 'inFilename' ]
            
            if not isinstance( filename, str ):
                OpenMaya.MGlobal.displayError( 'XML.readFile: inFilename is not a string.' )
                
        data = XMLData()
        xmlDoc = ElementTree.parse( filename )        
        xmlRoot = xmlDoc.getroot()
        
        for obj in xmlRoot.findall( 'object' ):
            newObj = XMLObject()
            newObj.name = obj.get( 'name' )
            newObj.type = obj.get( 'type' )
            newObj.icon = obj.get( 'icon' )
            elements = obj.findall( 'elem' )
            
            if elements:
                for elem in elements:
                    if newObj.type == 'Curve':
                        newElem = XMLCurveElement()
                        newElem.degree = int( elem.get( 'degree' ) )
                        newElem.form = int( elem.get( 'form' ) )
                        
                    else:
                        newElem = XMLElement()
                        
                    newElem.name = elem.get( 'name' )
                    newElem.type = elem.get( 'type' )
                    
                    
                    
                    attributes = elem.findall( 'attr' )
                    
                    if attributes:
                        for attr in attributes:
                            newAttr = XMLAttribute()
                            newAttr.name = attr.get( 'name' )
                            values = attr.findall( 'value' )
                            
                            if values:
                                for value in values:
                                    newValue = XMLValue()
                                    newValue.name = value.get( 'name' )
                                    newValue.text = value.text
                                    newValue.type = value.get( 'type' )
                                    newAttr.values.append( newValue )
                                    
                            newElem.attributes.append( newAttr )
                            
                    newObj.elements.append( newElem )
                    
            data.objects.append( newObj )
        
        if len( data.objects ) == 0:
            OpenMaya.MGlobal.displayWarning( 'XML.readFile: Nothing to read in the XML file provided.' )
            return None
            
        else:
            return data
                
'''
============================================================
--->>   XML DATA CLASS
============================================================
'''
class XMLData( object ):
    '''
    XML Data. Used to store the <data> from an XML file.
    '''
    def __init__( self ):
        # List that stores each element of the XML document.
        self.objects = []
        
    def getObjectByName( self, **kwargs ):
        '''
        Searches the document for the provided element by name.
        
        @param inName: String. Name of the element to search for.
        @return XMLElement object.
        '''
        if len( self.objects ) == 0:
            OpenMaya.MGlobal.displayInfo( 'No objects to search.' )
            
        elif 'inName' in kwargs:
            searchname = kwargs[ 'inName' ]
            
            for obj in self.objects:
                if obj.name == searchname:
                    return obj
        else:
            OpenMaya.MGlobal.displayError( 'XMLData.getObjectByName: No search name provided.' )
                
'''
============================================================
--->>   XML OBJECT CLASS
============================================================
'''
class XMLObject( object ):
    '''
    XML Object. Used to store the <object> data from an XML file.
    '''
    def __init__( self ):
        self.name = None
        self.type = None
        self.icon = None
        self.elements = []
        
    def getElementByName( self, **kwargs ):
        '''
        Searches the document for the provided element by name.
        
        @param inName: String. Name of the element to search for.
        @return XMLElement object.
        '''
        if len( self.elements ) == 0:
            OpenMaya.MGlobal.displayInfo( 'No elements to search.' )
            
        elif 'inName' in kwargs:
            searchname = kwargs[ 'inName' ]
            
            for elem in self.elements:
                if elem.name == searchname:
                    return elem
                
        else:
            OpenMaya.MGlobal.displayError( 'XMLObject.getElementByName: No search name provided.' )
        
        
'''
============================================================
--->>   XML ELEMENT CLASS
============================================================
'''
class XMLElement( object ):
    '''
    XML Element. Used to store the <element> data from an XML file.
    '''
    def __init__( self ):
        self.name = None#: Name of this element.
        self.type = None#: Type of this element.
        self.attributes = []#: List of attributes (sub-elements) of this element.
    
    def getAttributeByName( self, **kwargs ):
        '''
        Searches an element for the provided attribute by name.
        
        @param inName: String. Name of the attribute (subelement) to search for.
        @return: XMLAttribute object.
        '''
        if len( self.attributes ) == 0:
            OpenMaya.MGlobal.displayError( 'No attributes to search.' )
            
        elif 'inName' in kwargs:
            searchname = kwargs[ 'inName' ]
            
            for attr in self.attributes:
                if attr.name == searchname:
                    return attr
                
        else:
            OpenMaya.MGlobal.displayError( 'XMLElement.getAttributeByName: No search name provided.' )
    
class XMLCurveElement( XMLElement ):
    '''
    XML Element. Used to store the <element> data from a Curve XML file.
    '''
    def __init__( self ):
        self.degree = 1
        self.form = 1
        super( XMLCurveElement, self ).__init__()
        
        
'''
============================================================
--->>   XML ATTRIBUTE CLASS
============================================================
'''
class XMLAttribute( object ):
    '''
    XML Attribute. Used to store the <attribute> data from an XML file.
    '''
    def __init__( self ):
        self.name = None#: Name of the attribute.
        self.values = []#: List of value sub-elements in the attribute.
        
    def getValueByName( self, **kwargs ):
        '''
        Searches for a value by name.
        
        @param inName: String. Name of the value to search for.
        @return: XMLValue object.
        '''
        if len( self.values ) == 0:
            OpenMaya.MGlobal.displayError( 'No values to search.' )
            
        elif 'inName' in kwargs:
            searchname = kwargs[ 'inName' ]
            
            for value in self.values:
                if value.name == searchname:
                    return value
        
        else:
            OpenMaya.MGlobal.displayError( 'XMLAttribute.getValueByName: No search name provided.' )
        
'''
============================================================
--->>   XML VALUE CLASS
============================================================
'''
class XMLValue( object ):
    '''
    XML Value. Used to store the <value> data from an XML file.
    '''
    def __init__( self ):
        self.name = None#: Name of the value.
        self.text = None#: Text of the value.
        self.type = None#: Data type for this value.